#include <stdio.h>
#include <stdlib.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavutil/imgutils.h"
#include "libavutil/samplefmt.h"
#include "libavutil/opt.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include <libavcodec/avcodec.h>
}
//https://blog.51cto.com/990487026/1974942
//https://ciellee.blog.csdn.net/article/details/109065729
//./yuv2h264 tmp.yuv ttt.avi 640 480 400000 1
const char *inputFileName = NULL;
const char *outputFileName = NULL;
int frameWidth = 0;
int frameHeight = 0;
int bitRate = 0;
int frameTotal = 0;
FILE *pFileInput = NULL;
FILE *pFileOutput = NULL;

AVCodec *codec = NULL;
AVCodecContext *codecCtx = NULL;
AVFrame *frame = NULL;
AVPacket *pkt;

static int parse_input_paramaters(int argc, char **argv)
{
    inputFileName = argv[1];
    outputFileName = argv[2];

    pFileInput = fopen(inputFileName, "rb");
    if (NULL == pFileInput)
    {
        printf("open file ERR: [%s] \n", inputFileName);
        return -1;
    }

    pFileOutput = fopen(outputFileName, "wb+");
    if (NULL == pFileInput)
    {
        printf("open file ERR: [%s] \n", outputFileName);
        return -1;
    }

    frameWidth = atoi(argv[3]);
    frameHeight = atoi(argv[4]);
    bitRate = atoi(argv[5]);
    frameTotal = atoi(argv[6]);
    return 0;
}

static int read_yuv_data(int color)
{
    //color = 0 -> Y
    //color = 1 -> U
    //color = 2 -> V
    int color_height = color == 0 ? frameHeight : frameHeight / 2;
    int color_width = color == 0 ? frameWidth : frameWidth / 2;
    int color_size = color_height * color_width;
    int color_stride = frame->linesize[color];

    if (color_width == color_stride)
    {
        //printf("color_width == color_stride,color=[%d],color_stride=[%d] \n", color, color_stride);
        fread(frame->data[color], color_size, 1, pFileInput);
    }
    //else
    //{
    //    printf("color_width != color_stride,color=[%d] \n", color);
    //    int i;
    //    for(i = 0; i < color_height; i++)
    //    {
    //        fread(frame->data[color] + i * color_stride, color_width, 1, pFileInput);
    //    }
    //}
    return color_size;
}

void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket* pkt, FILE *pFileOutput)
{
    int ret = avcodec_send_frame(enc_ctx, frame);
    if (ret < 0)
    {
        fprintf(stderr, "Error sending a frame for encoding\n");
        exit(1);
    }
    while (ret >= 0)
    {
        ret = avcodec_receive_packet(enc_ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0)
        {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }
        //printf("Write packet of frame [%d], size=[%d] \n", i, pkt.size);
        //size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
        fwrite(pkt->data, 1, pkt->size, pFileOutput);
        av_packet_unref(pkt);
    }
}

int main(int argc, char **argv)
{
    if (parse_input_paramaters(argc, argv) == 0)
    {
        printf("inputFile:%s \n", inputFileName);
        printf("outputFile:%s \n", outputFileName);
        printf("Frame resolution::[%d*%d] \n", frameWidth, frameHeight);
        printf("freamToEncode=%d\n", frameTotal);
        printf("rate:%d \n", bitRate);
    }
    else
    {
        printf("init ERROR\n");
        return -1;
    }

    //avcodec_register_all();//注册编解码组件
    //avdevice_register_all();
    
    codec = (AVCodec *)avcodec_find_encoder(AV_CODEC_ID_H264); //查找编×××
    codec = (AVCodec *)avcodec_find_encoder_by_name("libx264");
    if (NULL == codec)
    {
        printf("find AV_CODEC_ID_H264 fail! \n");
        return -1;
    }

    //分配AVCodecContex实例
    codecCtx = avcodec_alloc_context3(codec);
    if (NULL == codecCtx)
    {
        printf("avcodec_alloc_context3  ERROR\n");
        return -1;
    }

    //设置编×××的参数
    codecCtx->width = frameWidth; //帧高
    codecCtx->height = frameHeight;
    codecCtx->bit_rate = bitRate; //比特率
   
    codecCtx->time_base = (AVRational){1, 25};
    codecCtx->time_base = (AVRational){25, 1};      //设置帧率
    codecCtx->gop_size = 10;
    codecCtx->max_b_frames = 1;
    codecCtx->pix_fmt = AV_PIX_FMT_YUV422P;//AV_PIX_FMT_YUYV422;
        if (av_opt_set(codecCtx->priv_data, "preset", "slow", 0) < 0) {//options
        printf("av_opt_set ERR\n");
        return -1;
    }
    // if (av_opt_set(codecCtx->priv_data, "preset", "slow", 0) < 0) {options
    //     printf("av_opt_set ERR\n");
    //     return -1;
    // }

    //打开编码器
    if (avcodec_open2(codecCtx, codec, NULL) < 0)
    {
        printf("avcodec_open2 ERR\n");
        return -1;
    }

    frame = av_frame_alloc();
    if (NULL == frame)
    {
        printf("av_frame_alloc err \n");
        return -1;
    }

    //分配AVframe及像素存储空间
    frame->width = codecCtx->width;
    frame->height = codecCtx->height;
    frame->format = codecCtx->pix_fmt;
    // if(av_p_w_picpath_alloc(frame->data, frame->linesize, frame->width, frame->height, frame->format, 32) < 0)//32 表示对齐
    // {
    //     printf("ERROR av_p_w_picpath_alloc\n");
    // }

    int i;
    for (i = 0; i < frameTotal; i++)
    {
        av_init_packet(pkt);
        pkt->data = NULL;
        pkt->size = 0;

        read_yuv_data(0);
        read_yuv_data(1);
        read_yuv_data(2);
        frame->pts = i;
        encode(codecCtx, frame, pkt, pFileOutput);
        //int got_packet;
        //int avcodec_encode_video2(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr);
        // if(avcodec_encode_video2(codecCtx, &pkt, (const AVFrame *)frame, &got_packet) < 0)
        // {
        //     printf("avcodec_encodec_video2 ERR \n");
        //     return -1;
        // }
    }
    encode(codecCtx, frame, pkt, pFileOutput);
    // int got_packet = 1;
    // while (got_packet)
    // {
    //     if (avcodec_encode_video2(codecCtx, &pkt, NULL, &got_packet) < 0)
    //     {
    //         printf("avcodec_encodec_video2 ERR \n");
    //         return -1;
    //     }
    //     if (got_packet)
    //     {
    //         printf("Write cache packet of frame [%d], size=[%d] \n", i, pkt.size);
    //         fwrite(pkt.data, 1, pkt.size, pFileOutput);
    //         av_packet_unref(&pkt);
    //     }
    // }

    fclose(pFileInput);
    fclose(pFileOutput);
    avcodec_close(codecCtx);
    av_free(codecCtx);
    av_freep(&frame->data[0]);
    av_frame_free(&frame);

    return 0;
}